<?php

/*
	[Discuz!] (C)2001-2007 Comsenz Inc.
	This is NOT a freeware, use is subject to license terms

	$Id: chinese.class.php 9806 2007-08-15 06:04:37Z cnteacher $
*/
///////////////////////////////////////////////////////////
//分析者：SamPeng
//分析时间：2008年11月11日15:53:13
//分析文件名：include/chinese.class.php
///////////////////////////////////////////////////////////

if(!defined('IN_DISCUZ')) {
        exit('Access Denied');
}
//定义常量
define('CODETABLE_DIR', DISCUZ_ROOT.'./include/tables/');

class Chinese {

	//定义常量并初始化
	var $table = '';
	var $iconv_enabled = false;
	var $unicode_table = array();
	var $config  =  array
		(
		'SourceLang'            => '',
		'TargetLang'            => '',
		'GBtoUnicode_table'     => 'gb-unicode.table',
		'BIG5toUnicode_table'   => 'big5-unicode.table',
		);
	/**
	 * 构造函数。对象建立时的初始化工作
	 *
	 * @param  $SourceLang 源语言包
	 * @param  $TargetLang 目标语言包
	 * @param  $ForceTable  强制转换的表格。。个人理解是这样
	 * @return Chinese对象
	 */
	function Chinese($SourceLang, $TargetLang, $ForceTable = FALSE) {
		//通过_lang方法，将语言包的名字格式化输出，并存储在配置数组中
		$this->config['SourceLang'] = $this->_lang($SourceLang);
		$this->config['TargetLang'] = $this->_lang($TargetLang);
		//说明：iconv是php内置的转码函数。
		if(function_exists('iconv') && $this->config['TargetLang'] != 'BIG5' && !$ForceTable) {
			$this->iconv_enabled = true;
		} else {
			$this->iconv_enabled = false;
			$this->OpenTable();
		}
	}
	//将语言包的名字进行格式化输出。
	function _lang($LangCode) {
		$LangCode = strtoupper($LangCode);
		//下面这段代码很简单，不用解释了
		if(substr($LangCode, 0, 2) == 'GB') {
			return 'GBK';
		} elseif(substr($LangCode, 0, 3) == 'BIG') {
			return 'BIG5';
		} elseif(substr($LangCode, 0, 3) == 'UTF') {
			return 'UTF-8';
		} elseif(substr($LangCode, 0, 3) == 'UNI') {
			return 'UNICODE';
		}
	}
	//还没看到这句是哪里调用的
	function _hex2bin($hexdata) {
		for($i=0; $i < strlen($hexdata); $i += 2) {
			$bindata .= chr(hexdec(substr($hexdata, $i, 2)));
		}
		return $bindata;
	}
	//打开字符表
	function OpenTable() {
		$this->unicode_table = array();
		//GBK转换Unicode
		if($this->config['SourceLang'] == 'GBK' || $this->config['TargetLang'] == 'GBK') {
			$this->table = CODETABLE_DIR.$this->config['GBtoUnicode_table'];
		} elseif($this->config['SourceLang'] == 'BIG5' || $this->config['TargetLang'] == 'BIG5') {
			//BIG转换的Unicode表
			$this->table = CODETABLE_DIR.$this->config['BIG5toUnicode_table'];
		}
		//打开文件
		$fp = fopen($this->table, 'rb');
		//读取文件
		$tabletmp = fread($fp, filesize($this->table));
		//遍历文件内容
		for($i = 0; $i < strlen($tabletmp); $i += 4) {
			//我英语不好哈。。看看手册上是这样解释的
			/**
			 * Unpack data from binary string
			 */
			//将数据从双重字符串中解包出来？返回的是数组类型
			$tmp = unpack('nkey/nvalue', substr($tabletmp, $i, 4));
			//因为我无法打开那个表。但是根据下面的判断，个人认为是解包成两个部分。一个部分是key，一个部分是value
			if($this->config['TargetLang'] == 'UTF-8') {
				//如果转换的目标是UTF-8。那字符表的关键就是Ox开头。然后跟上$tmp的value数组。。这个数组得从10进制转换成16进制。
				$this->unicode_table[$tmp['key']] = '0x'.dechex($tmp['value']);
			} elseif($this->config['SourceLang'] == 'UTF-8') {
				//同上。。只是后缀不是tmp中的value数组了，而是key数组。
				$this->unicode_table[$tmp['value']] = '0x'.dechex($tmp['key']);
			} elseif($this->config['TargetLang'] == 'UNICODE') {
				//不用根前缀咯
				$this->unicode_table[$tmp['key']] = dechex($tmp['value']);
			}
		}
	}
	//CHSU是什么的缩写？？？汗一个。。。
	function CHSUtoUTF8($c) {
		$str = '';
		if($c < 0x80) {
			$str .= $c;
		} elseif($c < 0x800) {
			$str .= (0xC0 | $c >> 6);
			$str .= (0x80 | $c & 0x3F);
		} elseif($c < 0x10000) {
			$str .= (0xE0 | $c >> 12);
			$str .= (0x80 | $c >> 6 & 0x3F);
			$str .=( 0x80 | $c & 0x3F);
		} elseif($c < 0x200000) {
			$str .= (0xF0 | $c >> 18);
			$str .= (0x80 | $c >> 12 & 0x3F);
			$str .= (0x80 | $c >> 6 & 0x3F);
			$str .= (0x80 | $c & 0x3F);
		}
		return $str;
	}
	//以上是位操作。。。看不明白。。无视
	
	/**
	 * 此类的重头：转换函数
	 *
	 * @param $SourceText 源文本
	 * @return 
	 */
	function Convert($SourceText) {
		if($this->config['SourceLang'] == $this->config['TargetLang']) {
			//源文件和目标文件的语言类型一样，当然不用转换了啊
			return $SourceText;
		} elseif($this->iconv_enabled) {
			//上面判断过是否存在开启了iconv这个函数。如果存在，那就利用php的iconv函数进行转换
			if($this->config['TargetLang'] <> 'UNICODE') {
				//当然，目标函数不能是unicode
				return iconv($this->config['SourceLang'], $this->config['TargetLang'], $SourceText);
			} else {
				/**
				 * 如果目标文本是unicode那怎么办呢？进行以下操作
				 */
				//初始化一个变量
				$return = '';
				while($SourceText != '') {
					//下面的语句说容易理解容易理解说不容易理解也不容易理解。。
					//我不知道字符格式转换是通过什么样的机理。。这里又用到了ord将字符串的ASCII码转换出来。。啊。这就是人生啊
					if(ord(substr($SourceText, 0, 1)) > 127) {
						$return .= "&#x".dechex($this->Utf8_Unicode(iconv($this->config['SourceLang'],"UTF-8", substr($SourceText, 0, 2)))).";";
						$SourceText = substr($SourceText, 2, strlen($SourceText));
					} else {
						$return .= substr($SourceText, 0, 1);
						$SourceText = substr($SourceText, 1, strlen($SourceText));
					}
				}
				return $return;
			}

		//目标语言为unicode的情况下，也就是任何的语言格式转换成unicode
		} elseif($this->config['TargetLang'] == 'UNICODE') {
			//和上面的处理没设么两样。
			$utf = '';
			while($SourceText != '') {
				if(ord(substr($SourceText, 0, 1)) > 127) {
					if($this->config['SourceLang'] == 'GBK') {
						//源语言为GBK
						$utf .= '&#x'.$this->unicode_table[hexdec(bin2hex(substr($SourceText, 0, 2))) - 0x8080].';';
					} elseif($this->config['SourceLang'] == 'BIG5') {
						//源语言为BIG5
						$utf .= '&#x'.$this->unicode_table[hexdec(bin2hex(substr($SourceText, 0, 2)))].';';
					}
					$SourceText = substr($SourceText, 2, strlen($SourceText));
				} else {
					//都不是的情况
					$utf .= substr($SourceText, 0, 1);
					$SourceText = substr($SourceText, 1, strlen($SourceText));
				}
			}
			return $utf;
		//看不下去了。。都是一些特有的位操作和转换。看得我一头雾水
		} else {
			$ret = '';
			//源文件为utf-8
			if($this->config['SourceLang'] == 'UTF-8') {
				$out = '';
				$len = strlen($SourceText);
				$i = 0;
				while($i < $len) {
					$c = ord(substr($SourceText, $i++, 1));
					switch($c >> 4) {
						case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
							$out .= substr($SourceText, $i - 1, 1);
							break;
						case 12: case 13:
							$char2 = ord(substr($SourceText, $i++, 1));
							$char3 = $this->unicode_table[(($c & 0x1F) << 6) | ($char2 & 0x3F)];
							//utf8转换GBK
							if($this->config['TargetLang'] == 'GBK') {
								$out .= $this->_hex2bin(dechex($char3 + 0x8080));
							} elseif($this->config['TargetLang'] == 'BIG5') {
								//uf8转换big5
								$out .= $this->_hex2bin($char3);
							}
							break;
						case 14:
							$char2 = ord(substr($SourceText, $i++, 1));
							$char3 = ord(substr($SourceText, $i++, 1));
							$char4 = $this->unicode_table[(($c & 0x0F) << 12) | (($char2 & 0x3F) << 6) | (($char3 & 0x3F) << 0)];
							if($this->config['TargetLang'] == 'GBK') {
								//utf8转换GBK
								$out .= $this->_hex2bin(dechex($char4 + 0x8080));
							} elseif($this->config['TargetLang'] == 'BIG5') {
								//utf8转换GBK
								$out .= $this->_hex2bin($char4);
							}
							break;
					}
				}
				return $out;
			} else {
				//源目标即不是utf-8也不是unicode
				while($SourceText != '') {
					if(ord(substr($SourceText, 0, 1)) > 127) {
						if($this->config['SourceLang'] == 'BIG5') {
							$utf8 = $this->CHSUtoUTF8(hexdec($this->unicode_table[hexdec(bin2hex(substr($SourceText, 0, 2)))]));
						} elseif($this->config['SourceLang'] == 'GBK') {
							$utf8=$this->CHSUtoUTF8(hexdec($this->unicode_table[hexdec(bin2hex(substr($SourceText, 0, 2))) - 0x8080]));
						}
						for($i = 0; $i < strlen($utf8); $i += 3) {
							$ret .= chr(substr($utf8, $i, 3));
						}
						$SourceText = substr($SourceText, 2, strlen($SourceText));
					} else {
						$ret .= substr($SourceText, 0, 1);
						$SourceText = substr($SourceText, 1, strlen($SourceText));
					}
				}
				//$this->unicode_table = array();
				$SourceText = '';
				return $ret;
			}
		}
	}

	function Utf8_Unicode($char) {
		//位操作。。。无视掉
		switch(strlen($char)) {
			case 1:
				return ord($char);
			case 2:
				$n = (ord($char[0]) & 0x3f) << 6;
				$n += ord($char[1]) & 0x3f;
				return $n;
			case 3:
				$n = (ord($char[0]) & 0x1f) << 12;
				$n += (ord($char[1]) & 0x3f) << 6;
				$n += ord($char[2]) & 0x3f;
				return $n;
			case 4:
				$n = (ord($char[0]) & 0x0f) << 18;
				$n += (ord($char[1]) & 0x3f) << 12;
				$n += (ord($char[2]) & 0x3f) << 6;
				$n += ord($char[3]) & 0x3f;
				return $n;
		}
	}

}

?>
